/*----------------------------------------------------------------------------
 * Copyright (c) <2016-2018>, <Huawei Technologies Co., Ltd>
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 * of conditions and the following disclaimer in the documentation and/or other materials
 * provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 * to endorse or promote products derived from this software without specific prior written
 * permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *---------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
 * Notice of Export Control Law
 * ===============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations, which might
 * include those applicable to Huawei LiteOS of U.S. and the country in which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in compliance with such
 * applicable export control laws and regulations.
 *---------------------------------------------------------------------------*/

#include "stm32l4xx.h"
#include "los_interrupt.h"
#include "los_event.h"
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

#define CN_RCV_RING_BUFLEN  128

static uint32_t s_uwIRQn = LPUART1_IRQn;

extern UART_HandleTypeDef hlpuart1;

//we make a ring here to storage the data
typedef struct {
    int size;
    int posW;
    int posR;
    int dataLen;
    unsigned char *buf;
} RingBuffer;
static RingBuffer *g_debugRingBuf;

/**
 * @brief: use this function to read data from ring buffer
 * @input: ringBuf, the ring buf to be read
 * @input: data, data to be read storaged
 * @return: 0 success while -1 failed
 */
static int RingBufRead(RingBuffer *buf, unsigned char *data)
{
    if (buf->dataLen == 0) {
        return -1;
    } else {
        *data = buf->buf[buf->posR];
        buf->posR = (buf->posR + 1) % buf->size;
        buf->dataLen--;
        return 0;
    }
}

/**
 * @brief: use this function to write data to ring buffer
 * @input: ringBuf, the ring buf to be read
 * @input: data, data to be written
 * @return: 0 success while -1 failed
 */
static int RingBufWrite(RingBuffer *buf, unsigned char data)
{
    if (buf->dataLen == buf->size) {
        return -1;
    } else {
        buf->buf[buf->posW] = data;
        buf->posW = (buf->posW + 1) % buf->size;
        buf->dataLen++;
        return 0;
    }
}

/**
 * @brief: use this function to make a ringbuffer
 * @return: the ring buffer maked, NULL failed while others success
 */
RingBuffer* RingBufInit(int size)
{
    RingBuffer *buf;
    if (size <= 0) {
        return NULL;
    }

    buf = malloc(size + sizeof(RingBuffer));

    if (buf != NULL) {
        buf->buf = (unsigned char*) buf + sizeof(RingBuffer);
        buf->dataLen = 0;
        buf->posR = 0;
        buf->posW = 0;
        buf->size = size;
    }
    return buf;
}

extern EVENT_CB_S g_shellInputEvent;

/**
 * @brief :This function used to deal the uart interrupt
 *        :when rxne interrupt happened, read data to the ring buffer
 *        :when idle interrupt happened, trigger the event to make the shell work
 * 
 */
static void hlpuart1_irq(void)
{
    unsigned char value;
    if (__HAL_UART_GET_FLAG(&hlpuart1, UART_FLAG_RXNE) != RESET) {
        value = (uint8_t) (hlpuart1.Instance->RDR & 0x00FF);
        RingBufWrite(g_debugRingBuf, value);
    } else if (__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) != RESET) {
        __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
        (void) LOS_EventWrite(&g_shellInputEvent, 0x1);
    }
}

/**
 * @brief :This function used to get data
 *        :This function works likes callback; when the  shell task is actived, it will 
 *        :read data until no data returned
 * return: 0 means no data and others means real input data
 */
int UartGetc(void)
{
    unsigned char data;

    if (0 == RingBufRead(g_debugRingBuf, &data)) {
        return (int) data;
    } else {
        return 0;
    }
}


/**
 * @brief : this is a shell test cammand,
 *        : we design a fucntion called, show the parameters input, and show its call times 
 * 
 */
#include "shell.h"
#include "los_debug.h"
static UINT32 TestShellFun(UINT32 argc, const CHAR **argv)
{
    int i = 0;
    static unsigned int counter = 0;
    for (i = 0; i < argc; i++) {
        PRINTK("arg:%d:%s\r\n", i, argv[i]);
    }
    PRINTK("counter:%d\r\n", counter++);

    return 0;
}

/**
 * @brief :This function used to register a test shell to the system
 *         and must be called after the shell initialize
 */
void  TestShellInit(void)
{
    osCmdReg(CMD_TYPE_EX, "test", XARGS, TestShellFun);
    return;
}

/**
 * @brief :This function used to initialize the uart
 *        :make a ringbuffer, register the uart interrupt and enable it, and initialize the shell
 *        :the shell init must be called for it is not called in the kernel initialize
 *        :This function must be called after the kernel initialize
 * return: 0 means no data and others means real input data
 */
int UartDebugInit(void)
{
    int ret = -1;
    g_debugRingBuf = RingBufInit(CN_RCV_RING_BUFLEN);
    if (g_debugRingBuf == NULL) {
        return ret;
    }
    LOS_HwiCreate(s_uwIRQn, 3, 0, hlpuart1_irq, 0);
    __HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_IDLE);
    __HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_RXNE);

    ret = LosShellInit(); // initialize the shell component
    OsShellInit(0); // initializ the system default command

    TestShellInit();  // register a test cammand
    return ret;
}

